
#include "../net/Broker4.h"
#include "../iface/APRInterface.h"
#include "Hash.h"
#include "SerialBlockSource.h"

SerialBlockSource::SerialBlockSource(const char *directorypath, apr_pool_t *imp)
{
   apr_status_t status;
   status = apr_pool_create(&mp, imp);
   CheckAPRError(status);

#if defined(__GNUC__) && (defined(__APPLE_CPP__) || defined(__APPLE_CC__) || defined(__NEXT_CPP__) || defined(__MACOS_CLASSIC__))
   const char *argv_for_pipe[] = { "/usr/bin/tar", "cf", "-", "-C", NULL, ".", NULL };
#else
   const char *argv_for_pipe[] = { "/bin/tar", "cf", "-", "-C", NULL, ".", NULL };
#endif
   if (argv_for_pipe[4] != NULL)
      errprint("path not null before specification!");
   directory_name = (char *)apr_palloc(mp, sizeof(char)*(strlen(directorypath)+1));
   strcpy(directory_name, directorypath);
   argv_for_pipe[4] = directory_name;

   status = apr_procattr_create(&pattr, mp);
   CheckAPRError(status);
   status = apr_procattr_io_set(pattr, APR_NO_PIPE, APR_FULL_BLOCK, APR_NO_PIPE);
   CheckAPRError(status);
   status = apr_procattr_cmdtype_set(pattr, APR_PROGRAM_ENV);
   CheckAPRError(status);
   status = apr_proc_create(&tproc, argv_for_pipe[0], (const char* const*)argv_for_pipe, NULL, pattr, mp);
   CheckAPRError(status);

   apr_sleep(1000000);
   int result;
   apr_exit_why_e why;
   status = apr_proc_wait(&tproc, &result, &why, APR_NOWAIT);
   if ((status != APR_CHILD_NOTDONE) && (why != APR_PROC_EXIT))
   {
      errprint("problem starting child process (%d, %d)", why, result);
      if (directory_name) free(directory_name);
      directory_name = NULL;
   }
   if ((status != APR_CHILD_NOTDONE) && (why == APR_PROC_EXIT) && (result != 0))
   {
      errprint("problem starting child process - result code non-zero (%d)", result);
      if (directory_name) free(directory_name);
      directory_name = NULL;
   }
}

SerialBlockSource::~SerialBlockSource()
{
   apr_status_t status;
   int result;
   apr_exit_why_e why;
   status = apr_file_close(tproc.out);
   CheckAPRError(status);
   apr_sleep(1000000);
   if (directory_name)
   {
      status = apr_proc_wait(&tproc, &result, &why, APR_NOWAIT);
      if (status == APR_CHILD_NOTDONE)
      {
#ifndef WAITFORPROC
         status = apr_proc_kill(&tproc, SIGTERM);
         CheckAPRError(status);
#endif
         status = apr_proc_wait(&tproc, &result, &why, APR_WAIT);
         if (status == APR_CHILD_NOTDONE)
            errprint("child process is still running");
      }
      if (result != 0)
         errprint("exit code from '%s' non-zero (%d)", "/bin/tar", result);
   }
   apr_pool_destroy(mp);
}

char *SerialBlockSource::GetNextBlock(int *realsize)
{
   if (directory_name == NULL) return NULL;
   if (realsize == NULL)
   {
      errprint("Can't get NULL block");
      return NULL;
   }
   char *mem_block = (char *)malloc(sizeof(char)*(*realsize));
   apr_size_t asize = (*realsize);

   apr_status_t status;
   status = apr_file_read_full(tproc.out, mem_block, asize, &asize);
   if (!(APR_STATUS_IS_EOF(status))) CheckAPRError(status);
   if (asize == 0) { free(mem_block); return NULL; }
   (*realsize) = asize;
   return mem_block;
}

char *SerialBlockSource::GetNextBlockEncrypted(const char *cname, KeyPair *kp, int *realsize, int *cesize, char **hash)
{
   if ((directory_name == NULL) || (kp == NULL))
   {
      if (!directory_name) errprint("no directory specified");
      if (!kp) errprint("cannot encrypt with no key");
      *cesize = 0;
      *realsize = 0;
      return NULL;
   }
   int newrealsize = *realsize;
   char *unenc_block = GetNextBlock(&newrealsize);
   if (unenc_block == NULL) { *cesize = 0; *realsize = 0; return NULL; }
   if (hash)
      *hash = Hash::getSHA1Hash(unenc_block, newrealsize);
   *realsize = newrealsize;
   std::vector<char> *enc_block_vec = Broker4::SimpleCryptDataChunk(cname, NULL, kp, NULL, unenc_block, newrealsize, 0, 1, mp, NULL);
   if (enc_block_vec == NULL) { *cesize = 0; return NULL; }
   free(unenc_block);
   int newlength = enc_block_vec->size();
   char *finalblock = (char *)malloc(sizeof(char)*newlength);
   memcpy(finalblock, &(enc_block_vec->at(0)), sizeof(char)*newlength);
   delete enc_block_vec;
   *cesize = newlength;
   return finalblock;
}

char *SerialBlockSource::GetNextBlockEncrypted(const char *encwith, KeyDB *kdb, int *realsize, int *cesize, char **hash)
{
   KeyPair *kp = kdb->GetPublicKeyByName(encwith);
   char *retstuff = GetNextBlockEncrypted(encwith, kp, realsize, cesize, hash);
   delete kp;
   return retstuff;
}
